#!/usr/bin/env ocaml

#use "topfind"

#require "cmdliner"

#require "fmt"

#require "fmt.tty"

#require "logs"

#require "logs.fmt"

#require "ppx_deriving.show"

#load "unix.cma"

#load "threads/threads.cma"

#require "feather"

open Cmdliner
module F = Feather
open Feather (* for |. *)

(*****************************************************************************)
(* Prelude *)
(*****************************************************************************)
(* Report the remaining lines of code (LOC) in pysemgrep to port and upload
 * the count to dashboard.semgrep.dev/metrics/semgrep.pysemgrep.loc
 *
 * usage:
 *   $ ./scripts/report_pysemgrep_loc --upload cli/src/semgrep/
 *
 * This is also run in a cron in .circleci/config.yml
 * Note that we're not using any semgrep libs here, just
 * pretty standard OCaml libraries.
 *
 * alternatives:
 *  - a bash script, like in report_test_metrics.sh
 *
 * See also ../tools/hello_script.ml.
 *)

(*****************************************************************************)
(* Types and constants *)
(*****************************************************************************)

let host = "https://dashboard.semgrep.dev"
let metric = "semgrep.pysemgrep.loc"

(* no more ppx_deriving_cmdliner :( see toplevel comment in hello_script.ml
 * old:
 * see https://github.com/hammerlab/ppx_deriving_cmdliner
 * type conf = {
 *   path : string; [@pos 0] [@docv "CMD"]
 *   upload : bool; [@default false]
 *   verbose : bool; [@default false]
 * }
 * [@@deriving cmdliner, show]
 *)

type conf = { path : string; upload : bool; verbose : bool } [@@deriving show]

(*****************************************************************************)
(* Helpers *)
(*****************************************************************************)

let upload conf loc =
  let url = Printf.sprintf "%s/api/metric/%s" host metric in
  (* TODO: use Logs library *)
  Logs.debug (fun m -> m "uploading to %s" url);

  let cmd =
    F.process "curl"
      [ "--fail"; "-L"; "-X"; "POST"; url; "-d"; string_of_int loc ]
  in
  F.run cmd

(*****************************************************************************)
(* Entry point *)
(*****************************************************************************)

let run conf =
  if conf.verbose then Logs.set_level (Some Logs.Debug);
  Logs.debug (fun m -> m "debug: params = %s" (show_conf conf));

  let out =
    (* alt: could also use F.find and F.grep builtins *)
    F.process "find" [ conf.path; "-type"; "f"; "-name"; "*.py" ]
    (* alt: could also use F.filter_lines *)
    |. F.process "grep" [ "-v"; "semgrep_interfaces/" ]
    |. F.process "grep" [ "-v"; "semdep/" ]
    (* --total is actually a recent flag so old 'wc' might not work *)
    |. F.process "xargs" [ "wc"; "-l"; "--total=only" ]
    |> F.collect F.stdout
  in
  let loc = int_of_string out in
  Logs.app (fun m ->
      m "LOC in %s (without semgrep_interfaces and semdep) = %d" conf.path loc);
  if conf.upload then upload conf loc

(*****************************************************************************)
(* Cmdliner boilerplate *)
(*****************************************************************************)

(* this used to be autogenerated by ppx_deriving_cmdliner *)
let conf_cmdliner_term : conf Term.t =
  let o_upload : bool Term.t =
    let doc = "" in
    Arg.(value & flag & info [ "u"; "upload" ] ~doc)
  in
  let o_verbose : bool Term.t =
    let doc = "" in
    Arg.(value & flag & info [ "v"; "verbose" ] ~doc)
  in
  let o_path : string Term.t =
    let doc = "" in
    Arg.(value & pos 0 string "TODO" & info [] ~docv:"CMD" ~doc)
  in
  let combine upload verbose path = { upload; verbose; path } in
  Term.(const combine $ o_upload $ o_verbose $ o_path)

let main () =
  (* logging setup *)
  Fmt_tty.setup_std_outputs ();
  Logs.set_reporter (Logs_fmt.reporter ());
  (* cmdliner setup *)
  let info = Cmd.info Sys.argv.(0) in
  let term = Term.(const run $ conf_cmdliner_term) in
  let cmd = Cmd.v info term in
  (* Let's go! *)
  exit (Cmd.eval cmd)

let () = main ()
